﻿using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace Oddo.Concept.Crm.Common.Communication.Http
{
    public class CompressedContent : HttpContent
    {
        private readonly HttpContent content;
        private readonly GZipCompressor compressor;


        public CompressedContent(HttpContent content)
        {
            this.content = content;
            this.compressor = new GZipCompressor();
            AddHeaders();
        }

        protected override bool TryComputeLength(out long length)
        {
            length = -1;
            return false;
        }

        protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            
            return content.ReadAsStreamAsync()
                .ContinueWith(st => compressor.CompressStream(st.Result, stream));
            
        }

        private void AddHeaders()
        {
            foreach (var header in content.Headers)
                Headers.TryAddWithoutValidation(header.Key, header.Value);

            Headers.ContentEncoding.Add(compressor.EncodingType);
        }
        
    }
    public interface ICompressor
    {
        string EncodingType { get; }
        Task Compress(Stream source, Stream destination);
        Task Decompress(Stream source, Stream destination);
    }

    public abstract class Compressor : ICompressor
    {
        public abstract string EncodingType { get; }
        public abstract Stream CreateCompressionStream(Stream output);
        public abstract Stream CreateDecompressionStream(Stream input);

        public virtual void CompressStream(Stream source, Stream destination)
        {
            var compressed = CreateCompressionStream(destination);
            source.CopyTo(compressed);
        }

        public virtual Task Compress(Stream source, Stream destination)
        {
            var compressed = CreateCompressionStream(destination);

            return Pump(source, compressed)
                .ContinueWith(task => compressed.Dispose());
        }

        public virtual Task Decompress(Stream source, Stream destination)
        {
            var decompressed = CreateDecompressionStream(source);

            return Pump(decompressed, destination)
                .ContinueWith(task => decompressed.Dispose());
        }

        protected virtual Task Pump(Stream input, Stream output)
        {
            var tcs = new TaskCompletionSource<object>();
            input.CopyTo(output);
            return tcs.Task;
        }
    }

    public class GZipCompressor : Compressor
    {
        private const string GZipEncoding = "gzip";

        public override string EncodingType
        {
            get { return GZipEncoding; }
        }

        
        public override Stream CreateCompressionStream(Stream output)
        {
            return new GZipStream(output, CompressionMode.Compress);
        }

        public override Stream CreateDecompressionStream(Stream input)
        {
            return new GZipStream(input, CompressionMode.Decompress);
        }
    }
}